/* Copyright (c) 2008 Google Inc.
*  Copyright (c) 2009 Pierre Henri Kuate.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if !DONT_USE_OAUTH_NET
using OAuth.Net.Common;
using OAuth.Net.Components;
#endif

namespace org.opensocial.client
{
	
	/// <summary> A utility object containing static methods for verifying the signature of
	/// requests signed by OpenSocial containers. All incoming requests should be
	/// verified in case a malicious third party attempts to submit fraudulent
	/// requests for user information.
	/// </summary>
	/// <author>  Jason Cooper
	/// </author>
    public static class OpenSocialRequestValidator
	{
		/// <summary> Validates the passed request by reconstructing the original URL and
		/// parameters and generating a signature following the OAuth HMAC-SHA1
		/// specification and using the passed secret key.
		/// </summary>
		/// <param name="request">Request containing required information for
		/// reconstructing the signature such as the request's URL
		/// components and parameters
		/// </param>
        /// <param name="knownGadgets">Contains the secret keys shared between application owner and
		/// container. Used by containers when issuing signed makeRequests
		/// and by client applications to verify the source of these
		/// requests and the authenticity of its parameters.
		/// </param>
		/// <param name="client">Client filled with information about the request</param>
		/// <returns> {@code true} if the signature generated in this function matches
		/// the signature in the passed request, {@code false} otherwise
		/// </returns>
		/// <throws>  IOException </throws>
        /// <throws>  URISyntaxException </throws>
        public static bool verifyHmacSignature(System.Web.HttpRequest request, System.Collections.Generic.Dictionary<string, Gadget> knownGadgets, out OpenSocialClient client)
        {
		    client = null;
            string consumerKey = request.QueryString[OpenSocialClient.Properties.CONSUMER_KEY];
            if (string.IsNullOrEmpty(consumerKey))
                return false;

            Gadget gadget;
            if (!knownGadgets.TryGetValue(consumerKey, out gadget))
                throw new OpenSocialException("Unknown consumer key: " + consumerKey);

            client = new OpenSocialClient(gadget);

            client.OwnerId = request.QueryString[OpenSocialClient.Properties.OWNER_ID];
            client.ViewerId = request.QueryString[OpenSocialClient.Properties.VIEWER_ID];
            client.AppId = request.QueryString[OpenSocialClient.Properties.APP_ID];
            client.AppUrl = request.QueryString[OpenSocialClient.Properties.APP_URL];
            client.View = request.QueryString[OpenSocialClient.Properties.VIEW];

            string oauthVersion = request.QueryString[Constants.VersionParameter];
            string timestamp = request.QueryString[Constants.TimestampParameter];
            string nonce = request.QueryString[Constants.NonceParameter];
            string signatureMethod = request.QueryString[Constants.SignatureMethodParameter];
            string signature = request.QueryString[Constants.SignatureParameter];

            if (!ValidateTimestamp(timestamp))
                return false;

            string signatureBase;
		    OpenSocialOAuthClient.ComputeRequestParameters(request.Url, null, request.HttpMethod, null,
                client.ViewerId, consumerKey, client.ConsumerSecret, null, null,
                oauthVersion, timestamp, nonce, signatureMethod, out signatureBase);

            ISigningProvider provider = new HmacSha1SigningProvider();
            return provider.CheckSignature(signatureBase, signature, client.ConsumerSecret, null);
        }


        private static bool ValidateTimestamp(string timestamp)
        {
            var timeout = UnixTime.ToUnixTime(System.DateTime.Now.AddDays(-1.1)); // TODO: Currently, use more than a day so that it works in any timezone; find a more precise solution
            int timestampValue;
            int.TryParse(timestamp, out timestampValue);
            return timestampValue > timeout; // Too old request => Replay attack // TODO: Is there a better way to detect it?
        }
	}
}
